using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace Curchy.PERTToolbox.PropertyGrid
{
	/// <summary>
	/// This control is in effect a read/write PictureBox. It supports a context menu
	/// with the usual Clipboard actions. Furthermore it supports the binding of the 
	/// Image property to a binary (byte[]) column in a DataSet and handles all neccessary
	/// conversion  
	/// </summary>
	[
	ToolboxBitmap(typeof(System.Windows.Forms.PictureBox))
	]
	public class DataPicture : System.Windows.Forms.UserControl,IImageContainer
	{
		#region Public Events
		/// <summary>
		/// Occurs when the Image property is changes
		/// </summary>
		public event EventHandler ImageChanged;

		/// <summary>
		/// Raises the ImageChanged event
		/// </summary>
		protected virtual void OnImageChanged()
		{
			if (ImageChanged!=null)
				ImageChanged(this,EventArgs.Empty);
		}

		#endregion Public Events

		#region Public Properties
		/// <summary>
		/// The image displayed in this DataPicture
		/// </summary>
		[
		Category("Appearance"),
		Description("The image displayed in this DataPicture")
		]
		public System.Drawing.Image Image
		{
			get
			{	return pictureBox1.Image;
			}
			set
			{
				if (pictureBox1.Image!=value)
				{
					pictureBox1.Image= value;
					OnImageChanged();
				}
			}
		}

		/// <summary>
		/// Controls how the picture box will handle image placement and control sizing.
		/// </summary>
		[
		Category("Behavior"),
		Description("Controls how the picture box will handle image placement and control sizing.")
		]
		public System.Windows.Forms.PictureBoxSizeMode SizeMode
		{
			get
			{	return this.pictureBox1.SizeMode;
			}
			set
			{	this.pictureBox1.SizeMode= value;
			}
		}

		#endregion Public Properties

		#region Public Methods
		/// <summary>
		/// Construct a DataPicture
		/// </summary>
		public DataPicture()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();
			this.SetStyle(ControlStyles.Selectable|ControlStyles.StandardClick,true);
			this.DataBindings.CollectionChanged+=new CollectionChangeEventHandler(DataBindings_CollectionChanged);
		}



		#endregion Public Methods

		#region Component Designer generated code
		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			this.pictureBox1 = new System.Windows.Forms.PictureBox();
			this.contextMenu1 = new Curchy.PERTToolbox.PropertyGrid.PictureBoxContextMenu(this.components);
			this.SuspendLayout();
			// 
			// pictureBox1
			// 
			this.pictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
			this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
			this.pictureBox1.Location = new System.Drawing.Point(0, 0);
			this.pictureBox1.Name = "pictureBox1";
			this.pictureBox1.Size = new System.Drawing.Size(150, 150);
			this.pictureBox1.TabIndex = 0;
			this.pictureBox1.TabStop = false;
			this.pictureBox1.Click += new System.EventHandler(this.pictureBox1_Click);
			this.pictureBox1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseDown);
			// 
			// DataPicture
			// 
			this.ContextMenu = this.contextMenu1;
			this.Controls.Add(this.pictureBox1);
			this.Name = "DataPicture";
			this.ResumeLayout(false);

		}
		#endregion

		#region Overriden Functionality
		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		/// <summary>
		/// Ensure that it is obvious when this control is focused.
		/// </summary>
		protected override bool ShowFocusCues
		{
			get
			{
				return true;
			}
		}

		/// <summary>
		/// Change the backgound colour to highlight the focus on this control
		/// </summary>
		/// <param name="e">Contains the event data</param>
		protected override void OnEnter(EventArgs e)
		{
			base.OnEnter (e);
			this.pictureBox1.BackColor= SystemColors.Highlight;
		}

		/// <summary>
		/// Remove the Focus Indication
		/// </summary>
		/// <param name="e">Contains the event data</param>
		protected override void OnLeave(EventArgs e)
		{
			base.OnLeave (e);
			this.pictureBox1.BackColor= this.BackColor;
		}

		/// <summary>
		/// Ensure that this control focuses when clicked
		/// </summary>
		/// <param name="e">Contains the event data</param>
		protected override void OnMouseDown(MouseEventArgs e)
		{
			base.OnMouseDown (e);
			this.Focus();
		}

		/// <summary>
		/// Ensure that this control focuses when clicked
		/// </summary>
		/// <param name="e">Contains the event data</param>
		protected override void OnClick(EventArgs e)
		{
			base.OnClick (e);
			this.Focus();
		}

		#endregion Overriden Functionality

		#region Private Functionality

		/// <summary>
		/// Ensure that when the contained PictureBox is clicked that this control gets the focus
		/// </summary>
		/// <param name="sender">The sender of the event</param>
		/// <param name="e">Contains the event data</param>
		private void pictureBox1_Click(object sender, System.EventArgs e)
		{
			this.Focus();
		}

		/// <summary>
		/// Ensure that when the contained PictureBox is clicked that this control gets the focus
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e">Contains the event data</param>
		private void pictureBox1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			this.Focus();
		}

		/// <summary>
		/// Check whether a Binding for Image has been added and if so
		/// attach event handlers to handle data conversion
		/// </summary>
		/// <param name="sender">The sender of the event</param>
		/// <param name="e">Contains the event data</param>
		private void DataBindings_CollectionChanged(object sender, CollectionChangeEventArgs e)
		{
			Binding binding = this.DataBindings["Image"];
			if (binding!=null)
			{	binding.Format+=new ConvertEventHandler(binding_Format); 
				binding.Parse+=new ConvertEventHandler(binding_Parse);
			}
		}

		/// <summary>
		/// An event handler to handle the conversion from binary (byte[]) to Image
		/// of data entering the control
		/// </summary>
		/// <param name="sender">The sender of the event</param>
		/// <param name="ev">Contains the event data</param>
		private void binding_Format(object sender, ConvertEventArgs ev)
		{
			if (!(ev.Value is Image) && ev.DesiredType==typeof(Image))
			{
				byte[] img = ev.Value as byte[];
				ev.Value= sEmptyBitmap;
				if (img!=null && img.Length>0)
				{
					// mPreviousBuffer= img;
					System.IO.MemoryStream ms = new System.IO.MemoryStream();
					try
					{
						int offset = 0;
						ms.Write(img, offset, img.Length - offset);
						Bitmap bmp = new Bitmap(ms);
						ms.Close();
						ev.Value= bmp;
						// mPreviousBitmap= bmp;
					}
					catch (Exception)
					{
					}
				}
			}
		}

		/// <summary>
		/// An event handler to handle the conversion from Image to binary (byte[])
		/// of data leaving the control
		/// </summary>
		/// <param name="sender">The sender of the event</param>
		/// <param name="ev">Contains the event data</param>
		private void binding_Parse(object sender, ConvertEventArgs ev)
		{
			if ((ev.Value is Bitmap) && ev.DesiredType==typeof(byte[]))
			{
				Bitmap bmp= (Bitmap)ev.Value;
				// if (bmp==mPreviousBitmap)
				//	ev.Value= mPreviousBuffer;
				//else
				{
					System.IO.MemoryStream ms = new System.IO.MemoryStream();
					System.Drawing.Imaging.ImageFormat format= bmp.RawFormat;
					if (format==System.Drawing.Imaging.ImageFormat.MemoryBmp)
						format= System.Drawing.Imaging.ImageFormat.Bmp;
					bmp.Save(ms,format);
					ms.Close();
					ev.Value= (Byte[])ms.GetBuffer();
				}
			}
			else if (ev.Value==null || ev.Value==sEmptyBitmap)
				ev.Value=System.DBNull.Value;
		}

		/// <summary>
		/// Contained control to display the image
		/// </summary>
		private System.Windows.Forms.PictureBox pictureBox1;
		/// <summary>
		/// Context menu for clipboard work 
		/// </summary>
		private Curchy.PERTToolbox.PropertyGrid.PictureBoxContextMenu contextMenu1; 
		/// <summary>
		/// Auto-generated by VS
		/// </summary>
		private System.ComponentModel.IContainer components;
		/// <summary>
		/// Empty Bitmap "constant" used as required
		/// </summary>
		private static Bitmap sEmptyBitmap= new Bitmap(1,1); 
		
		// The following was needed at one point to get around a problem with data conversion in binding
		// private System.Drawing.Bitmap mPreviousBitmap;
		// byte[] mPreviousBuffer;

		#endregion Private Functionality
	}
}
